﻿package editor
{	
	import efnx.sound.Waveform;
	import efnx.events.WaveformEvent;
	import efnx.gfx.Raster;
	
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.media.Sound;
	import flash.media.SoundChannel;	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	public class WavePlotter extends Sprite
	{
		public const SOUND_COMPLETE:String = 'SOUND_COMPLETE';
		
		private var FWaveform:Bitmap;
		private var FPlotter:Waveform;
		private var FData:BitmapData;
		private var FDx:int;
		private var FSound:Sound;
		private var FChannel:SoundChannel;
		private var FPlayhead:Bitmap;
		private var FWidth:Number;
		private var FHeight:Number;
		private var FPlaying:Boolean;
		
		private var FAmplitudesData:Vector.<Point>;
		
		public function WavePlotter(AWidth:Number, AHeight:Number):void
		{
			FWidth = AWidth;
			FHeight = AHeight;
			FAmplitudesData = new Vector.<Point>;
		}
		
		private function Reset():void
		{
			removeEventListener(Event.ENTER_FRAME, OnEnterFrame);
			removeEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
			FAmplitudesData = new Vector.<Point>;
			
			if(FPlotter)
				FPlotter.cancel();
			
			if(FChannel)
				Stop();
				
			while(numChildren>0)
				removeChildAt(numChildren-1);
						
			FDx = 0;
			
			FWaveform = new Bitmap();
			FWaveform.bitmapData = new BitmapData(FWidth, FHeight, true, 0x00FFFFFF);
			addChild(FWaveform);
			FPlayhead = new Bitmap();
			FPlayhead.bitmapData = new BitmapData(1, FHeight, true, 0xFF000000);
			addChild(FPlayhead);
			
			FData = FWaveform.bitmapData;			
			FPlotter = new Waveform();			
		}
		
		public function set Sound(ASound:Sound)
		{
			Reset();
			
			FSound = ASound;			
			FPlotter.sound = FSound;			
			FPlotter.leftSample = 0;
			FPlotter.rightSample = FSound.length * 44.1;
			FPlotter.numWindows = FData.width;
			FPlotter.createWaveform("rms");
			FPlotter.addEventListener(WaveformEvent.PROGRESS, OnWindowAnalyze);
			FPlotter.addEventListener(WaveformEvent.COMPLETE, OnAnalyzeComplete);
		}
		
		private function OnAnalyzeComplete(e:WaveformEvent):void
		{
			dispatchEvent(new AnalyzeCompleteEvent(FAmplitudesData));
			addEventListener(Event.ENTER_FRAME, OnEnterFrame);
			addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
		}
		
		private function OnWindowAnalyze(e:WaveformEvent):void
		{			
			var h:int = FData.height;
			
			var l:Number = e.leftChunk.length;		
			var lv:Number = new Number();
			var rv:Number = new Number()
			
			for (var i:int = 0; i < l; i++)
			{
				var vData:Point = new Point();
				
				lv = e.leftChunk[i];
				rv = e.rightChunk[i];
				vData.x = lv;
				vData.y = rv;
				
				Raster.line(FData, FDx+i, h/2 - h * Math.abs(lv), FDx+i, h/2, 0xFFFF9933);				
				Raster.line(FData, FDx+i, h/2 + h * Math.abs(rv), FDx+i, h/2, 0xFF333333);				
				
				FAmplitudesData.push(vData);
			}
			
			FDx += i;
		}	
		
		public function Play():void
		{
			FPlaying = true;
			FChannel = FSound.play((FPlayhead.x * FSound.length) / FWaveform.width);					
		}
		
		public function Pause():void
		{
			FPlaying = false;
			FChannel.stop();			
		}
		
		public function Stop():void
		{
			FChannel.stop();
			FPlaying = false;
			FPlayhead.x = 0;
		}
				
		public function OnEnterFrame(e:Event):void
		{
			if(FChannel && FPlaying)
			{
				var loopSamplePosition:uint = FChannel.position * 44.1 - FPlotter.leftSample;
				var samplesInLoop:uint = FPlotter.rightSample - FPlotter.leftSample;
				FPlayhead.x = FWaveform.width * loopSamplePosition/samplesInLoop;
				if(FPlayhead.x >= FWaveform.width)
				{
					Stop();
					dispatchEvent(new Event(SOUND_COMPLETE));
				}
			}
		}
			
		private function OnMouseDown(e:MouseEvent):void
		{
			if(FPlaying)
			{
				FChannel.stop();
				FChannel = FSound.play((e.localX * FSound.length) / FWaveform.width);
			}
			else
				FPlayhead.x = e.localX;
		}
		
		public function get Playing():Boolean
		{
			return FPlaying;
		}
		
		public function get Position():Number
		{
			return ((FPlayhead.x * FSound.length) / FWaveform.width) / FSound.length;
		}
		
		public function set Position(APosition:Number)
		{
			FPlayhead.x = FWaveform.width * APosition * FSound.length / FSound.length;
		}
		
		public function get Time():String
		{			
			var vTime = (FPlayhead.x * FSound.length) / FWaveform.width;
			var vSeconds = Math.floor(vTime / 1000);
			var vMinutesString:String = Math.floor(vSeconds / 60).toString();
			var vSecondsString:String = (vSeconds % 60).toString();
			if(vMinutesString.length < 2)			
				vMinutesString = '0' + vMinutesString;
			if(vSecondsString.length < 2)			
				vSecondsString = '0' + vSecondsString;
			return vMinutesString + ':' + vSecondsString;			
		}
		
		public function get TotalTime():String
		{
			var vTime = FSound.length;
			var vSeconds = Math.floor(vTime / 1000);
			var vMinutesString:String = Math.floor(vSeconds / 60).toString();
			var vSecondsString:String = (vSeconds % 60).toString();
			if(vMinutesString.length < 2)			
				vMinutesString = '0' + vMinutesString;
			if(vSecondsString.length < 2)			
				vSecondsString = '0' + vSecondsString;
			return vMinutesString + ':' + vSecondsString;		
		}
		
		public function get Amplitudes():Vector.<Point>
		{
			return FAmplitudesData;
		}
	}	
}
